#include <clusnet/base/Exception.h>
#include <clusnet/base/DynamicBuffer.h>

#include <cassert>

using namespace clusnet::base;


const char DynamicBuffer::digits[] = "9876543210123456789";
const char DynamicBuffer::digitsHex[] = "0123456789ABCDEF";

DynamicBuffer::DynamicBuffer(size_t initialSize)
    : data_(nullptr),
      len_(initialSize),
      cur_(0)
{
    data_ = static_cast<char*>(std::malloc(len_));
    if (data_ == nullptr)
    {
        len_ = 0;
        throw Exception("malloc failed");
    }
}

DynamicBuffer::~DynamicBuffer()
{
    destroy();
}

DynamicBuffer::DynamicBuffer(const DynamicBuffer& that)
    : data_(nullptr),
      len_(0),
      cur_(0)
{
    // 深拷贝
    reset(that.data_, that.cur_, that.len_);
}

DynamicBuffer::DynamicBuffer(DynamicBuffer&& that)
    : data_(that.data_),
      len_(that.len_),
      cur_(that.cur_)
{
    that.data_ = nullptr;
    that.len_ = 0;
    that.cur_ = 0;
}

DynamicBuffer& DynamicBuffer::operator= (const DynamicBuffer& that)
{
    if (this == &that)
    {
        return *this;
    }
    reset(that.data_, that.cur_, that.len_);
    return *this;
}

DynamicBuffer& DynamicBuffer::operator= (DynamicBuffer&& that)
{
    if (this == &that)
    {
        return *this;
    }
    destroy();
    assert(data_ == nullptr && cur_ == 0 && len_ == 0);
    data_ = that.data_;
    len_ = that.len_;
    cur_ = that.cur_;
    that.data_ = nullptr;
    that.len_ = 0;
    that.cur_ = 0;
    return *this;
}

void DynamicBuffer::append(const char* buf, size_t len)
{
    if (avail() >= len)
    {
        std::memcpy(data_ + cur_, buf, len);
        cur_ += len;
    }
    else
    {
        assert(len_ > 0);
        size_t newSize = (len / len_ + 2) * len_;
        resize(newSize);
        std::memcpy(data_ + cur_, buf, len);
        cur_ += len;
    }
}

void DynamicBuffer::resize(size_t newSize)
{
    assert(data_ != nullptr && newSize > 0);
    auto temp = std::realloc(data_, newSize);
    if (temp == nullptr)
    {
        throw Exception("realloc failed");
    }
    data_ = static_cast<char*>(temp);
    len_ = newSize;
    cur_ = std::min(cur_, len_);
}

// 深拷贝
void DynamicBuffer::reset(const char* buf, size_t cur, size_t len)
{
    destroy();
    assert(data_ == nullptr && len_ == 0 && cur_ == 0);
    data_ = static_cast<char*>(std::malloc(len));
    if (data_ == nullptr)
    {
        throw Exception("malloc failed");
    }
    std::memcpy(data_, buf, cur);
    cur_ = cur;
    len_ = len;
}

void DynamicBuffer::destroy()
{
    if (data_ != nullptr)
    {
        std::free(data_);
    }
    data_ = nullptr;
    len_ = 0;
    cur_ = 0;
}
